/*
 * Copyright 2010-2013, David George, Licensed under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.magneato.utils;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Representation of a directory of templates in memory
 * 
 * @author David B George
 */
public class TemplateGroup {
	private File templateDir;
	private Map<String, Template> templates;
	private long lastModified = 0;

	private final Log _logger = LogFactory.getLog(TemplateGroup.class);

	public static final String PREQUEL = "x.";
	public static final String EXT = ".vm";

	/**
	 * If template group not found what do we do?
	 * 
	 * @param path
	 * @param nameSpace
	 * @throws TemplateGroupNotFoundException 
	 */
	public TemplateGroup(String path, String nameSpace) throws TemplateGroupNotFoundException {
		templateDir = new File(path + "/" + nameSpace);
		if (!templateDir.exists() && !templateDir.isDirectory()) {
			_logger.error("Template directory not found " + path);
			throw new TemplateGroupNotFoundException(path + "/" + nameSpace);
		}
		templates = new HashMap<String, Template>();
		lastModified = 0;
		updateTemplates();
		if (templates.isEmpty()) {
			_logger.error("No templates in directory " + path);
			throw new TemplateGroupNotFoundException(path + "/" + nameSpace);
		}
	}

	public Template getTemplate(String templateName) {
		updateTemplates();
		return templates.get(templateName);
	}

	/**
	 * return list of templates, note that changes to the set (e.e. delete) will
	 * affect the the templates map.
	 * 
	 * @return
	 */
	public Set<String> getTemplateList() {
		return templates.keySet();
	}

	/**
	 * Returns child templates corresponding to the templateName.
	 * 
	 * NB: an unfriendly caller could change our internal template data !!!
	 * 
	 * @param referrerTemplate
	 *            - Name of the template who's children we want to find.
	 *            Children are defined in the parent header. Null if there was
	 *            no parent.
	 * @return A collection of children defined for this template
	 */
	public Collection<Template> getChildTemplates(String referrerTemplate) {
		updateTemplates();

		ArrayList<Template> templateList = null;
		if (referrerTemplate == null) {
			templateList = new ArrayList<Template>();
			for (Map.Entry<String, Template> entry : templates.entrySet()) {
				templateList.add(entry.getValue());
			}// for

			return templateList;
		}

		Template t = templates.get(referrerTemplate);
		if (t == null) {
			_logger.error("No template for referrer " + referrerTemplate);
			return null; // or throw exception
		}
		
		String childTemplates = t.getChildren();
		if ("*".equals(childTemplates)) {
			templateList = new ArrayList<Template>();
			for (Map.Entry<String, Template> entry : templates.entrySet()) {
				templateList.add(entry.getValue());
			}
		}

		String[] children = childTemplates.split(",");
		templateList = new ArrayList<Template>();
		for (String child : children) {
			child = child.trim();
			Template c = templates.get(child);
			if (c != null) {
				templateList.add(c);
			} else {
				_logger.warn("Could not find child template " + child);
			}
		}

		return templateList;
	}

	/**
	 * Update the templates in a directory if they have changed
	 * 
	 * @param templateDir
	 * 
	 * @see org.magneato.utils.TemplateI#getTemplates(java.lang.String)
	 */
	private synchronized void updateTemplates() {

		if (templateDir.lastModified() > lastModified) {
			lastModified = templateDir.lastModified();

			for (File file : templateDir.listFiles(new TemplateFilter())) {
				String templateName = getTemplateName(file.getName());

				// does template already exist?
				Template t = templates.get(templateName);
				if (t == null) {
					t = new Template(templateName);
				}

				try {
					t.parseTemplateFile(file);
					// check view template exists
					File display = new File(templateDir.getAbsolutePath() + "/"
							+ t.getDisplay() + EXT);
					if (!display.exists()) {
						_logger.error("No view " + display.getAbsolutePath()
								+ " for template " + file.getName());
					}

					templates.put(templateName, t);
				} catch (Exception e) {
					_logger.error("problem parsing template file "
							+ file.getName() + " because " + e.getMessage());
					templates.remove(templateName);
				}
			}// for
		}
	}

	
	/*
	 * @returns filename stripped of the extension - we let the ViewResolver
	 * find the correct template based on the name
	 */
	private String getTemplateName(String fileName) {
		int end = fileName.lastIndexOf('.');
		if (end > 0) {
			return fileName.substring(PREQUEL.length(), end);
		}
		return null;
	}
}